/*
 * Copyright (c)  2008 Sebastien Tremblay
 *
 * id3-tag-builder - settings.XmlApplicationSettings.java
 *
 * All source and documentation is copyrighted by Sebastien Tremblay
 * (seb at ryders dot net) and made available under the Apache License 2.0.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * $Id: XmlApplicationSettings.java 33 2010-04-02 07:47:55Z ryders $
 */
package com.empireone.id3tagbuilder.settings;

import com.empireone.id3tagbuilder.classes.Format;
import com.empireone.id3tagbuilder.classes.Path;
import com.empireone.id3tagbuilder.helpers.XmlHelper;
import java.io.File;
import java.io.IOException;
import java.util.Locale;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * The <code>XmlApplicationSettings</code> class is used for the application to
 * retrieve and store settings in an XML file.
 *
 * @author Sebastien Tremblay
 */
public class XmlApplicationSettings implements ApplicationSettings {

    private String fileName;
    private Document doc;
    private String version;

    /**
     *
     * @param fileName
     * @param version
     * @throws ApplicationSettingsException
     */
    public XmlApplicationSettings(String fileName, String version) throws ApplicationSettingsException {

        this.fileName = fileName;
        this.version = version;

        this.init();
    }

    private void init() throws ApplicationSettingsException {

        try {

            DocumentBuilderFactory fct = DocumentBuilderFactory.newInstance();

            doc = fct.newDocumentBuilder().parse(fileName);

            Node root = XmlHelper.getNodeByName(doc.getDocumentElement().getChildNodes(), "settings");

            if (root == null) {
                throw new ApplicationSettingsException("The configuration file (" + fileName + ") is corrupted or missing the root <settings> element.");
            } else {
                checkSettingsVersion(XmlHelper.getAttributeByName(root, "version"), version);
            }


            if (doc.getElementsByTagName("path").getLength() == 0) {
                throw new ApplicationSettingsException("The configuration file (" + fileName + ") contains no defined Paths.");
            }

            if (doc.getElementsByTagName("format").getLength() == 0) {
                throw new ApplicationSettingsException("The configuration file (" + fileName + ") contains no defined Formats.");
            }

        } catch (ParserConfigurationException ex) {
        } catch (SAXException ex) {

            throw new ApplicationSettingsException("The configuration file (" + fileName + ") could not be parsed as an xml document.");
        } catch (IOException ex) {

            throw new ApplicationSettingsException("The configuration file (" + fileName + ") was not found or cannot be read.");
        }
    }

    private void checkSettingsVersion(Node node, String version) {

        if (node != null) {
            float appVers = Float.parseFloat(version);
            float settingsVers = Float.parseFloat(node.getNodeValue());

        // do we want to compare the two versions and display a message
        // if the user is not running the latest - pls upgrade?
        // not a prob - defaults will handle missing entries.
        }
    }

    /**
     *
     * @return
     */
    @Override
    public Vector<Format> getFormats() {

        Vector<Format> formats = new Vector<Format>();

        NodeList nodes = doc.getElementsByTagName("format");

        for (int i = 0; i < nodes.getLength(); i++) {

            Node id = XmlHelper.getAttributeByName(nodes.item(i), "id");
            Node name = XmlHelper.getAttributeByName(nodes.item(i), "name");
            Node expr = XmlHelper.getNodeByName(nodes.item(i).getChildNodes(), "expr");

            if (id != null && name != null && expr != null && expr.hasChildNodes()) {
                Format f = new Format(Integer.parseInt(id.getNodeValue()), name.getNodeValue(), expr.getFirstChild().getNodeValue());
                if (f.isValid())
                    formats.add(f);
                //else
                    // notify the user?
            } else {
                // log...
            }
        }
        return formats;
    }

    /**
     *
     * @return
     */
    @Override
    public Vector<Path> getPaths() {

        Vector<Path> paths = new Vector<Path>();

        NodeList nodes = doc.getElementsByTagName("path");

        for (int i = 0; i < nodes.getLength(); i++) {

            Node id = XmlHelper.getAttributeByName(nodes.item(i), "id");
            Node name = XmlHelper.getAttributeByName(nodes.item(i), "name");
            Node uri = XmlHelper.getAttributeByName(nodes.item(i), "uri");
            Node depth = XmlHelper.getAttributeByName(nodes.item(i), "depth");

            if (id != null && name != null && uri != null) {
                if (depth == null) {
                    paths.add(new Path(Short.parseShort(id.getNodeValue()), name.getNodeValue(), uri.getNodeValue()));
                } else {
                    paths.add(new Path(Short.parseShort(id.getNodeValue()), name.getNodeValue(), uri.getNodeValue(), Short.parseShort(depth.getNodeValue())));
                }
            } else {
                // log...
            }
        }
        return paths;
    }

    /**
     *
     * @return
     */
    @Override
    public Locale getLocale() {

        String value = getSetting("locale", DefaultSettings.getLocale());

        if (value.indexOf("_") <= 0) {
            return new Locale(value);
        } else {
            String[] tmp = value.split("_");
            return new Locale(tmp[0], tmp[1]);
        }
    }

    /**
     *
     * @return
     */
    @Override
    public int getAlbumArtSize() {
        return getSettingInt("artSizeWarn", DefaultSettings.getAlbumArtSize());
    }

    /**
     *
     * @return
     */
    @Override
    public String getWinManager() {

        return getSetting("windowManager", DefaultSettings.getWinManager());
    }

    /**
     *
     * @return
     */
    @Override
    public boolean getMaintainMusicDb() {
        return getSettingBool("musicDb", DefaultSettings.maintainMusicDb());
    }

    /**
     *
     * @param value
     */
    @Override
    public void setMaintainMusicDb(boolean value) {
        setSetting("musicDb", value ? "true" : "false");
    }

/*    protected void initMusicDb() {
        
    }*/

    /**
     *
     * @return
     */
    @Override
    public boolean getDisplayDetails() {
        return getSettingBool("details", DefaultSettings.displayDetails());
    }

    /**
     *
     * @param value
     */
    @Override
    public void setDisplayDetails(boolean value) {
        this.setSetting("displayDetails", value ? "true" : "false");
    }

    /**
     *
     * @return
     */
    @Override
    public boolean getWriteId3v1() {
        return getSettingBool("id3v1", DefaultSettings.writeId3v1());
    }

    /**
     *
     * @param value
     */
    @Override
    public void setWriteId3v1(boolean value) {
        this.setSetting("id3v1", value ? "true" : "false");
    }

    /**
     *
     * @return
     */
    @Override
    public boolean getWriteId3v2() {
        return getSettingBool("id3v2", DefaultSettings.writeId3v2());
    }

    /**
     *
     * @param value
     */
    @Override
    public void setWriteId3v2(boolean value) {
        this.setSetting("id3v2", value ? "true" : "false");
    }

    /**
     *
     * @return
     */
    @Override
    public boolean getNormTrackNo() {
        return getSettingBool("normTrackNo", DefaultSettings.writeMultiSets());
    }

    /**
     *
     * @param value
     */
    @Override
    public void setWriteTrackDiscNo(boolean value) {
        this.setSetting("trackDiscNo", value ? "true" : "false");
    }

    /**
     *
     * @return
     */
    @Override
    public boolean getCleanupTags() {
        return getSettingBool("cleanupTags", DefaultSettings.cleanupTags());
    }

    /**
     *
     * @param value
     */
    @Override
    public void setCleanupTags(boolean value) {
        this.setSetting("cleanupTags", value ? "true" : "false");
    }

    /**
     *
     * @return
     */
    @Override
    public String getArtFilenames() {
        return getSetting("albumArtFilenames", DefaultSettings.getArtFilenames());
    }

    /**
     *
     * @return
     */
    @Override
    public boolean getEmbedArt() {
        return getSettingBool("embedArt", DefaultSettings.embedArt());
    }

    /**
     *
     * @param value
     */
    @Override
    public void setEmbedArt(boolean value) {
        this.setSetting("embedArt", value ? "true" : "false");
    }

    /**
     *
     * @return
     */
    @Override
    public String getTagLib() {
        return getSetting("tagLib", DefaultSettings.getTagLib());
    }

    /**
     *
     */
    @Override
    public void save() {
        try {
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();

            transformer.setOutputProperty
                    ("{http://xml.apache.org/xslt}indent-amount", "4");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");

            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");

            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(new File(fileName));

            transformer.transform(source, result);
        } catch (TransformerException ex) {
            Logger.getLogger(XmlApplicationSettings.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //<editor-fold defaultstate="collapsed" desc=" get/set setting [protected] ">
    /**
     *
     * @param name
     * @param defaultValue
     * @return
     */
    protected String getSetting(String name, String defaultValue) {
        Node node = XmlHelper.getNodeByName(doc.getElementsByTagName(name), name);

        if (node != null && node.hasChildNodes()) {
            String value = node.getFirstChild().getNodeValue();
            if (value != null) {
                return value;
            }
        }

        return defaultValue;
    }

    /**
     *
     * @param name
     * @param defaultValue
     * @return
     */
    protected boolean getSettingBool(String name, boolean defaultValue) {
        Node node = XmlHelper.getNodeByName(doc.getElementsByTagName(name), name);

        if (node != null && node.hasChildNodes()) {
            String value = node.getFirstChild().getNodeValue();
            if (value != null) {
                return value.equalsIgnoreCase("true") || value.equals("1");
            }
        }

        return defaultValue;
    }

    /**
     *
     * @param name
     * @param defaultValue
     * @return
     */
    protected int getSettingInt(String name, int defaultValue) {
        Node node = XmlHelper.getNodeByName(doc.getElementsByTagName(name), name);

        if (node != null && node.hasChildNodes()) {
            String value = node.getFirstChild().getNodeValue();
            if (value != null) {
                try {
                    return Integer.parseInt(value);
                } catch (NumberFormatException ex) {
                }
            }
        }

        return defaultValue;
    }

    /**
     *
     * @param name
     * @param value
     */
    protected void setSetting(String name, String value) {

        Node node = XmlHelper.getNodeByName(doc.getElementsByTagName(name), name);

        if (node == null) {
            node = doc.createElement(name);
            doc.getElementsByTagName("settings").item(0).appendChild(node);
        }

        node.setTextContent(value);
    }

    /**
     *
     * @return
     */
    @Override
    public String getMusicDbUri() {
        return getSetting("musicDbUri", DefaultSettings.getMusicDbUri());
    }

    /**
     *
     * @return
     */
    @Override
    public String getMusicDbType() {
        return getSetting("musicDbType", DefaultSettings.getMusicDbType());
    }

    /**
     *
     * @return
     */
    @Override
    public String getVersion() {
        return this.version;
    }
    
    //</editor-fold>
}
